<!doctype html><html lang=en-us dir=ltr><head><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="主从架构-主从同步 # 在Redis中，主从同步是一种常用的数据复制方式，它允许一个或多个从服务器（slave）获得与主服务器（master）相同的数据副本。这种架构提供了数据的冗余和读取扩展性。
全量复制：当一个从服务器第一次连接到主服务器时，或是由于某些原因需要重新同步时，会进行全量复制。在这个过程中，主服务器会生成一个当前所有数据的快照，并将这个快照发送至请求同步的从服务器。从服务器接收到数据后，加载到自己的数据空间内。
部分复制：一旦完成了全量复制，如果从服务器断开连接又重新连接，且中断时间不长，主服务器可以只发送这段时间内发生变化的数据给从服务器，而不是再次进行全量复制。这依赖于主服务器的复制积压缓冲区来存储最近的写命令。
同步策略：为了保证数据的一致性，从服务器在初始同步完成之前不会对外提供服务。在日常运行时，主服务器会将写命令同时发送给所有的从服务器，以此来保证数据的实时一致性。
主从同步使得从服务器可以承担读操作，减轻主服务器的负载，同时也可以在主服务器遇到故障时，进行故障转移。
主从哨兵架构 # Redis哨兵（Sentinel）系统是用于管理多个Redis服务器的系统。该体系结构具有以下特点：
监控：哨兵会监控主从服务器是否正常运作。 自动故障转移：如果主服务器出现故障，哨兵可以自动选举新的主服务器，并让原来的从服务器指向新的主服务器。 配置提供者：哨兵还会将当前的主服务器地址提供给客户端，确保客户端总是连接到正确的主服务器。 哨兵机制通过这些功能增加了Redis环境的高可用性和稳定性。
切片集群-Redis Cluster # Redis Cluster是Redis的分布式解决方案。它支持数据的水平分片，以下是其关键特性：
自动分片：��动将数据分布在不同的节点上，每个节点只保存整个数据集的一部分。 高可用性：采用主从复制模型，即使在多个节点失败的情况下也能保证服务的可用性。 无中心设计：没有中心节点，每个节点都保存着整个集群状态的一部分，节点之间通过Gossip协议交换信息。 Redis Cluster通过对键进行CRC16计算并对16384取余数来决定将键分配到哪个槽位，每个节点负责一部分槽位，从而实现负载均衡。
Redis Cluster 通信开销 # 由于Redis Cluster节点间需要频繁地交换消息以维护集群状态，因此会产生额外的通信开销：
Gossip通信：节点间通过Gossip协议定期交换信息，包括数据迁移、故障检测等。 重定向操作：客户端可能会尝试向不包含数据所在槽的节点发起请求，这时节点会返回一个重定向信息 切片集群-Codis
Redis 分布式锁是一种用于多个计算节点之间同步访问共享资源的机制。在分布式系统中，当多个进程需要同时访问某些数据或执行某些任务时，为了避免竞态条件（race conditions）和数据不一致，通常需要使用分布式锁来保证在同一时间只有一个进程能够执行特定的操作。
Redis 分布式锁实现 # 一个常见的Redis分布式锁实现是使用 Redis 的 SETNX 命令（Set if not exists）。这个命令只在键不存在的情况下设置键的值，并且返回是否成功设置。由于 SETNX 是原子操作，因此可以用来实现锁的功能。
另一个更加推荐的方式是使用 Redis 2.6.12 版本引入的 SET 命令结合选项 NX（表示只有键不存在时才进行设置）和 PX（给键设置过期时间，单位为毫秒），这可以保证即使在客户端崩溃的情况下，锁也会在一定时间后自动释放，防止死锁。
使用 Redis 分布式锁的步骤： # 尝试获取锁
使用 SET key value NX PX milliseconds 进行设置。 如果返回 OK，则获取锁成功。 如果返回 nil，则获取锁失败。 执行业务逻辑"><meta name=theme-color content="#FFFFFF"><meta name=color-scheme content="light dark"><meta property="og:title" content="Redis进阶"><meta property="og:description" content="主从架构-主从同步 # 在Redis中，主从同步是一种常用的数据复制方式，它允许一个或多个从服务器（slave）获得与主服务器（master）相同的数据副本。这种架构提供了数据的冗余和读取扩展性。
全量复制：当一个从服务器第一次连接到主服务器时，或是由于某些原因需要重新同步时，会进行全量复制。在这个过程中，主服务器会生成一个当前所有数据的快照，并将这个快照发送至请求同步的从服务器。从服务器接收到数据后，加载到自己的数据空间内。
部分复制：一旦完成了全量复制，如果从服务器断开连接又重新连接，且中断时间不长，主服务器可以只发送这段时间内发生变化的数据给从服务器，而不是再次进行全量复制。这依赖于主服务器的复制积压缓冲区来存储最近的写命令。
同步策略：为了保证数据的一致性，从服务器在初始同步完成之前不会对外提供服务。在日常运行时，主服务器会将写命令同时发送给所有的从服务器，以此来保证数据的实时一致性。
主从同步使得从服务器可以承担读操作，减轻主服务器的负载，同时也可以在主服务器遇到故障时，进行故障转移。
主从哨兵架构 # Redis哨兵（Sentinel）系统是用于管理多个Redis服务器的系统。该体系结构具有以下特点：
监控：哨兵会监控主从服务器是否正常运作。 自动故障转移：如果主服务器出现故障，哨兵可以自动选举新的主服务器，并让原来的从服务器指向新的主服务器。 配置提供者：哨兵还会将当前的主服务器地址提供给客户端，确保客户端总是连接到正确的主服务器。 哨兵机制通过这些功能增加了Redis环境的高可用性和稳定性。
切片集群-Redis Cluster # Redis Cluster是Redis的分布式解决方案。它支持数据的水平分片，以下是其关键特性：
自动分片：��动将数据分布在不同的节点上，每个节点只保存整个数据集的一部分。 高可用性：采用主从复制模型，即使在多个节点失败的情况下也能保证服务的可用性。 无中心设计：没有中心节点，每个节点都保存着整个集群状态的一部分，节点之间通过Gossip协议交换信息。 Redis Cluster通过对键进行CRC16计算并对16384取余数来决定将键分配到哪个槽位，每个节点负责一部分槽位，从而实现负载均衡。
Redis Cluster 通信开销 # 由于Redis Cluster节点间需要频繁地交换消息以维护集群状态，因此会产生额外的通信开销：
Gossip通信：节点间通过Gossip协议定期交换信息，包括数据迁移、故障检测等。 重定向操作：客户端可能会尝试向不包含数据所在槽的节点发起请求，这时节点会返回一个重定向信息 切片集群-Codis
Redis 分布式锁是一种用于多个计算节点之间同步访问共享资源的机制。在分布式系统中，当多个进程需要同时访问某些数据或执行某些任务时，为了避免竞态条件（race conditions）和数据不一致，通常需要使用分布式锁来保证在同一时间只有一个进程能够执行特定的操作。
Redis 分布式锁实现 # 一个常见的Redis分布式锁实现是使用 Redis 的 SETNX 命令（Set if not exists）。这个命令只在键不存在的情况下设置键的值，并且返回是否成功设置。由于 SETNX 是原子操作，因此可以用来实现锁的功能。
另一个更加推荐的方式是使用 Redis 2.6.12 版本引入的 SET 命令结合选项 NX（表示只有键不存在时才进行设置）和 PX（给键设置过期时间，单位为毫秒），这可以保证即使在客户端崩溃的情况下，锁也会在一定时间后自动释放，防止死锁。
使用 Redis 分布式锁的步骤： # 尝试获取锁
使用 SET key value NX PX milliseconds 进行设置。 如果返回 OK，则获取锁成功。 如果返回 nil，则获取锁失败。 执行业务逻辑"><meta property="og:type" content="article"><meta property="og:url" content="http://example.org/docs/programmer/cloudnative/redis/advanced-knowledge-of-redis/"><meta property="article:section" content="docs"><meta property="article:published_time" content="2024-05-05T00:00:00+00:00"><meta property="article:modified_time" content="2024-05-05T00:00:00+00:00"><title>Redis进阶 | Ian's Blog</title>
<link rel=manifest href=/manifest.json><link rel=icon href=/favicon.png type=image/x-icon><link rel=stylesheet href=/book.min.c58292d36b18b675680ab9baea2029204537b839ea72f258746ec0f32ce8d6c8.css integrity="sha256-xYKS02sYtnVoCrm66iApIEU3uDnqcvJYdG7A8yzo1sg=" crossorigin=anonymous><script defer src=/flexsearch.min.js></script><script defer src=/en.search.min.80b0c12c6a3982c23bab5eb1d10c75b4cf8bfbaa1c7e183729648ce8a09207d7.js integrity="sha256-gLDBLGo5gsI7q16x0Qx1tM+L+6ocfhg3KWSM6KCSB9c=" crossorigin=anonymous></script></head><body dir=ltr><input type=checkbox class="hidden toggle" id=menu-control>
<input type=checkbox class="hidden toggle" id=toc-control><main class="container flex"><aside class=book-menu><div class=book-menu-content><nav><h2 class=book-brand><a class="flex align-center" href=/><span>Ian's Blog</span></a></h2><div class=book-search><input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/><div class="book-search-spinner hidden"></div><ul id=book-search-results></ul></div><ul><li class=book-section-flat><a href=/docs/programmer/>程序员笔记</a><ul><li><input type=checkbox id=section-d5f99046a51e5e750b61f2e037945fcc class=toggle>
<label for=section-d5f99046a51e5e750b61f2e037945fcc class="flex justify-between"><a role=button>基础工具和配置</a></label><ul><li><a href=/docs/programmer/basetc/%E6%96%87%E6%9C%AC%E4%B8%89%E5%89%91%E5%AE%A2/>文本三剑客</a></li><li><a href=/docs/programmer/basetc/tmux/>Tmux使用笔记</a></li><li><a href=/docs/programmer/basetc/obsidian%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%E6%8F%92%E4%BB%B6/>Obsidian思维导图插件</a></li><li><a href=/docs/programmer/basetc/for_china/>各个软件换源</a></li><li><a href=/docs/programmer/basetc/tipsofvim/>tip Of vim</a></li><li><a href=/docs/programmer/basetc/editer/>编辑器使用</a></li><li><a href=/docs/programmer/basetc/bash/>Bash</a></li><li><a href=/docs/programmer/basetc/gitbook/>Gitbook</a></li><li><a href=/docs/programmer/basetc/vim/>Vim</a></li><li><a href=/docs/programmer/basetc/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B/>supervisor</a></li></ul></li><li><input type=checkbox id=section-8658298e10b544e890095f646916165a class=toggle checked>
<label for=section-8658298e10b544e890095f646916165a class="flex justify-between"><a href=/docs/programmer/cloudnative/>云原生</a></label><ul><li><input type=checkbox id=section-31d3b02fd4a132635e483e7e756058ea class=toggle checked>
<label for=section-31d3b02fd4a132635e483e7e756058ea class="flex justify-between"><a href=/docs/programmer/cloudnative/redis/>中间件</a></label><ul><li><a href=/docs/programmer/cloudnative/redis/python-redis-%E5%AE%A2%E6%88%B7%E7%AB%AF/>Python Redis 客户端</a></li><li><a href=/docs/programmer/cloudnative/redis/bigkey-and-hotkey/>大key、热key问题</a></li><li><a href=/docs/programmer/cloudnative/redis/the-basics-of-redis/>Redis基础</a></li><li><a href=/docs/programmer/cloudnative/redis/advanced-knowledge-of-redis/ class=active>Redis进阶</a></li><li><a href=/docs/programmer/cloudnative/redis/%E4%BD%BF%E7%94%A8%E5%91%BD%E4%BB%A4/>Redis常用命令</a></li></ul></li><li><input type=checkbox id=section-62d608ed890b3abc76dae78ccfcab912 class=toggle>
<label for=section-62d608ed890b3abc76dae78ccfcab912 class="flex justify-between"><a role=button>k8s</a></label><ul><li><a href=/docs/programmer/cloudnative/k8s/elk%E5%9C%A8k8s%E4%B8%8A%E7%9A%84%E9%83%A8%E7%BD%B2%E4%BD%BF%E7%94%A8%E7%A4%BA%E4%BE%8B/>elk在k8s上的部署使用示例</a></li><li><a href=/docs/programmer/cloudnative/k8s/k8s-%E9%85%8D%E5%A5%97%E8%AF%B4%E6%98%8E/>k8s 配套说明</a></li><li><a href=/docs/programmer/cloudnative/k8s/k8s-%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/>k8s技术分享</a></li><li><a href=/docs/programmer/cloudnative/k8s/k8s%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E5%92%8C%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90/>k8s学习-常用命令和配置文件</a></li><li><a href=/docs/programmer/cloudnative/k8s/argo-workflow%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%92%8C%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E5%88%86%E6%9E%90/>Argo Workflow性能测试和使用场景分析</a></li><li><a href=/docs/programmer/cloudnative/k8s/argo-%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/>Argo 使用记录</a></li></ul></li><li><input type=checkbox id=section-b828bf3d116bc282da9db25a06bf908e class=toggle>
<label for=section-b828bf3d116bc282da9db25a06bf908e class="flex justify-between"><a role=button>中间件</a></label><ul><li><a href=/docs/programmer/cloudnative/middleware/kafka-%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8/>Kafka 安装和使用</a></li></ul></li><li><a href=/docs/programmer/cloudnative/%E5%AE%B9%E5%99%A8/note-of-podman/>Podman</a></li><li><a href=/docs/programmer/cloudnative/nginx%E5%AE%9E%E7%94%A8%E9%85%8D%E7%BD%AE/>Nginx实用配置</a></li><li><a href=/docs/programmer/cloudnative/uwsgi-%E5%A4%84%E7%90%86%E8%AE%B0%E5%BD%95/>uwsgi 处理记录</a></li><li><a href=/docs/programmer/cloudnative/note-of-docker/>Docker</a></li><li><a href=/docs/programmer/cloudnative/%E5%AE%B9%E5%99%A8/note-of-docker/>Docker</a></li><li><a href=/docs/programmer/cloudnative/fastapi/>Django的建站的(｡･･)ﾉﾞ</a></li><li><a href=/docs/programmer/cloudnative/nginx-%E9%AB%98%E5%8F%AF%E7%94%A8/>Nginx高可用</a></li><li><a href=/docs/programmer/cloudnative/notesdjango/>Django的建站的(｡･･)ﾉﾞ</a></li><li><a href=/docs/programmer/cloudnative/sonar-%E4%BB%A3%E7%A0%81%E9%9D%99%E6%80%81%E6%A3%80%E6%9F%A5/>Sonar 代码静态检查</a></li></ul></li><li><input type=checkbox id=section-883e27361d38e16afb68faff3435ac0b class=toggle>
<label for=section-883e27361d38e16afb68faff3435ac0b class="flex justify-between"><a role=button>机器学习</a></label><ul><li><a href=/docs/programmer/ml/stable-diffusion/>AI画图</a></li><li><a href=/docs/programmer/ml/%E7%88%AC%E8%99%AB/>爬虫</a></li><li><a href=/docs/programmer/ml/paddle/>Paddle</a></li><li><a href=/docs/programmer/ml/tensorflow/>Tensorflow</a></li><li><a href=/docs/programmer/ml/opencv/>OpenCV</a></li><li><a href=/docs/programmer/ml/yolo/>Demo Test项目中的一些东西</a></li><li><a href=/docs/programmer/ml/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%B8%B8%E7%94%A8%E5%BA%93%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95/>机器学习库</a></li></ul></li><li><input type=checkbox id=section-0bf4f4329214e20fa67ca3e12c6aad0c class=toggle>
<label for=section-0bf4f4329214e20fa67ca3e12c6aad0c class="flex justify-between"><a role=button>平台</a></label><ul><li><a href=/docs/programmer/platforms/ubuntu/>Ubuntu</a></li><li><a href=/docs/programmer/platforms/%E9%98%BF%E9%87%8C%E4%BA%91%E4%BD%BF%E7%94%A8/>阿里云使用</a></li><li><a href=/docs/programmer/platforms/wps-for-linux/>WPS for Linux</a></li><li><a href=/docs/programmer/platforms/appsflyer/>AppsFlyer-外网移动归因营销分析平台</a></li></ul></li><li><input type=checkbox id=section-bf4e0d6f0b81f7b3ec08ed1fc66b874d class=toggle>
<label for=section-bf4e0d6f0b81f7b3ec08ed1fc66b874d class="flex justify-between"><a href=/docs/programmer/langs/>编程语言</a></label><ul><li><input type=checkbox id=section-771df6c720301e69f1715f7fc174ac3d class=toggle>
<label for=section-771df6c720301e69f1715f7fc174ac3d class="flex justify-between"><a role=button>Python</a></label><ul><li><a href=/docs/programmer/langs/python/sqlalchemy/>SqlAlchemy - 数据库Orm</a></li><li><a href=/docs/programmer/langs/python/pypi/>PyPi使用说明</a></li><li><a href=/docs/programmer/langs/python/pytest/>PyTest</a></li><li><a href=/docs/programmer/langs/python/paramiko-%E4%BD%BF%E7%94%A8-sshsftp/>Paramiko 使用 Ssh&amp;sftp</a></li><li><a href=/docs/programmer/langs/python/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%A4%9A%E8%BF%9B%E7%A8%8B/>Python 多线程多进程</a></li><li><a href=/docs/programmer/langs/python/notespython/>Python 常用库</a></li><li><a href=/docs/programmer/langs/python/notespython/>Python 笔记</a></li><li><a href=/docs/programmer/langs/python/py%E5%B0%8F%E5%B7%A5%E5%85%B7%E5%92%8C%E5%8A%9F%E8%83%BD%E6%80%A7%E6%96%B9%E6%B3%95/>Py小工具和功能性方法</a></li><li><a href=/docs/programmer/langs/python/notespython/>解决问题</a></li></ul></li><li><input type=checkbox id=section-9f8ac8f06e138c7ac13ff61f23b4d497 class=toggle>
<label for=section-9f8ac8f06e138c7ac13ff61f23b4d497 class="flex justify-between"><a role=button>Golang</a></label><ul><li><a href=/docs/programmer/langs/golang/advanced-knowledge-of-golang/>Golang进阶笔记</a></li><li><a href=/docs/programmer/langs/golang/noteofgoexp/>Golang进阶笔记</a></li><li><a href=/docs/programmer/langs/golang/note-of-golang/>Golang笔记</a></li><li><a href=/docs/programmer/langs/golang/noteofgolang/>Golang笔记</a></li></ul></li><li><a href=/docs/programmer/langs/cmake/>CMake 使用Tips</a></li><li><a href=/docs/programmer/langs/tips-of-debugers/>Tips of debuggers</a></li><li><a href=/docs/programmer/langs/tips-of-markdown/>Tips of MarkDown</a></li><li><a href=/docs/programmer/langs/java/notesjava/>愉快的Java(happy to learn the fuck java)</a></li><li><a href=/docs/programmer/langs/note-for-fmtdata/>数据格式笔记</a></li></ul></li><li><input type=checkbox id=section-de7bfad1d124522974cdf8addfbb23f2 class=toggle>
<label for=section-de7bfad1d124522974cdf8addfbb23f2 class="flex justify-between"><a role=button>Net</a></label><ul><li><a href=/docs/programmer/net/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/>网络编程</a></li><li><a href=/docs/programmer/net/nginx%E5%AE%9E%E7%94%A8%E9%85%8D%E7%BD%AE/>Nginx实用配置</a></li><li><a href=/docs/programmer/net/tips-of-grpc/>gRpc使用小记</a></li><li><a href=/docs/programmer/net/epoll%E5%AE%9E%E7%8E%B0/>Epoll实现</a></li><li><a href=/docs/programmer/net/net/>Epoll实现</a></li></ul></li><li><input type=checkbox id=section-4446dd07527142b855f26d7cc8f0e617 class=toggle>
<label for=section-4446dd07527142b855f26d7cc8f0e617 class="flex justify-between"><a href=/docs/programmer/database/>Database</a></label><ul><li><a href=/docs/programmer/database/mongodb/>Mongodb使用记录</a></li><li><a href=/docs/programmer/database/dgraph/>Dgraph使用小记</a></li><li><a href=/docs/programmer/database/note-of-db-data-mongodb/>数据库-MongoDB篇</a></li><li><a href=/docs/programmer/database/note-of-db-data/>数据库-MongoDB篇</a></li><li><a href=/docs/programmer/database/note-of-db-data-mysql/>数据库-MySQL篇</a></li></ul></li><li><input type=checkbox id=section-7e5360c5e7954906b897ed79085884b6 class=toggle>
<label for=section-7e5360c5e7954906b897ed79085884b6 class="flex justify-between"><a href=/docs/programmer/gui/>图形用户界面-GUI</a></label><ul><li><a href=/docs/programmer/gui/pyinstaller-python%E6%89%93%E5%8C%85/>python打包</a></li><li><a href=/docs/programmer/gui/pyinstaller/>python打包</a></li><li><a href=/docs/programmer/gui/qt/>Qt/PySide</a></li><li><a href=/docs/programmer/gui/noteofvn_py/>Vn.Py学习笔记（Python交易平台框架）</a></li><li><a href=/docs/programmer/gui/vn.py%E7%AC%94%E8%AE%B0-%E4%BA%A4%E6%98%93%E5%B9%B3%E5%8F%B0%E5%AE%A2%E6%88%B7%E7%AB%AF/>Vn.Py学习笔记（Python交易平台框架）</a></li><li><a href=/docs/programmer/gui/notespython/>图形化界面 （Python Gui）</a></li><li><a href=/docs/programmer/gui/notespython/>图形化界面 （Python Gui）</a></li></ul></li><li><input type=checkbox id=section-ddcbe632dc99a9fb372422dada8ee641 class=toggle>
<label for=section-ddcbe632dc99a9fb372422dada8ee641 class="flex justify-between"><a role=button>OS操作系统问题处理</a></label><ul><li><a href=/docs/programmer/os/install_some/>安装问题</a></li><li><a href=/docs/programmer/os/android/>安卓</a></li><li><a href=/docs/programmer/os/freebsd/>FreeBSD</a></li><li><a href=/docs/programmer/os/note-of-linux/>Linux 笔记</a></li><li><a href=/docs/programmer/os/npm/>Npm</a></li><li><a href=/docs/programmer/os/git/>Git</a></li><li><a href=/docs/programmer/os/problem-of-windows/>Windows 爬坑记</a></li><li><a href=/docs/programmer/os/tips-of-problems/>解决问题记录笔记</a></li></ul></li><li><input type=checkbox id=section-d325c59fc6513e1b1e05a60b192d4973 class=toggle>
<label for=section-d325c59fc6513e1b1e05a60b192d4973 class="flex justify-between"><a href=/docs/programmer/hardware/>硬件</a></label><ul><li><a href=/docs/programmer/hardware/raspberrypi/>Raspberry Pi</a></li><li><a href=/docs/programmer/hardware/screen/>Screen</a></li></ul></li></ul></li><li class=book-section-flat><span>建模和游戏</span><ul><li><a href=/docs/3dgame/blender/>Blender</a></li><li><a href=/docs/3dgame/noteofue4/>UE4 笔记</a></li></ul></li><li class=book-section-flat><a href=/docs/example/>Hugo特殊Markdown语法说明</a><ul><li><a href=/docs/example/table-of-contents/>Table of Contents</a><ul><li><a href=/docs/example/table-of-contents/with-toc/>With ToC</a></li><li><a href=/docs/example/table-of-contents/without-toc/>Without ToC</a></li></ul></li><li><a href=/docs/example/shortcodes/buttons/>Buttons</a></li><li><a href=/docs/example/shortcodes/columns/>Columns</a></li><li><a href=/docs/example/shortcodes/details/>Details</a></li><li><a href=/docs/example/shortcodes/expand/>Expand</a></li><li><a href=/docs/example/shortcodes/hints/>Hints</a></li><li><a href=/docs/example/shortcodes/tabs/>Tabs</a></li></ul></li></ul><ul><li><a href=/posts/>Blog</a></li></ul></nav><script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script></div></aside><div class=book-page><header class=book-header><div class="flex align-center justify-between"><label for=menu-control><img src=/svg/menu.svg class=book-icon alt=Menu>
</label><strong>Redis进阶</strong>
<label for=toc-control><img src=/svg/toc.svg class=book-icon alt="Table of Contents"></label></div><aside class="hidden clearfix"><nav id=TableOfContents><ul><li><a href=#主从架构-主从同步><strong>主从架构-主从同步</strong></a></li><li><a href=#主从哨兵架构><strong>主从哨兵架构</strong></a></li><li><a href=#切片集群-redis-cluster><strong>切片集群-Redis Cluster</strong></a></li><li><a href=#redis-cluster-通信开销><strong>Redis Cluster 通信开销</strong></a><ul><li><a href=#redis-分布式锁实现>Redis 分布式锁实现</a></li><li><a href=#使用-redis-分布式锁的步骤>使用 Redis 分布式锁的步骤：</a></li><li><a href=#注意事项>注意事项</a></li><li><a href=#python-实现示例>Python 实现示例</a></li></ul></li></ul></nav></aside></header><article class=markdown><h2 id=主从架构-主从同步><strong>主从架构-主从同步</strong>
<a class=anchor href=#%e4%b8%bb%e4%bb%8e%e6%9e%b6%e6%9e%84-%e4%b8%bb%e4%bb%8e%e5%90%8c%e6%ad%a5>#</a></h2><p>在Redis中，主从同步是一种常用的数据复制方式，它允许一个或多个从服务器（slave）获得与主服务器（master）相同的数据副本。这种架构提供了数据的冗余和读取扩展性。</p><ol><li><p><strong>全量复制</strong>：当一个从服务器第一次连接到主服务器时，或是由于某些原因需要重新同步时，会进行全量复制。在这个过程中，主服务器会生成一个当前所有数据的快照，并将这个快照发送至请求同步的从服务器。从服务器接收到数据后，加载到自己的数据空间内。</p></li><li><p><strong>部分复制</strong>：一旦完成了全量复制，如果从服务器断开连接又重新连接，且中断时间不长，主服务器可以只发送这段时间内发生变化的数据给从服务器，而不是再次进行全量复制。这依赖于主服务器的复制积压缓冲区来存储最近的写命令。</p></li><li><p><strong>同步策略</strong>：为了保证数据的一致性，从服务器在初始同步完成之前不会对外提供服务。在日常运行时，主服务器会将写命令同时发送给所有的从服务器，以此来保证数据的实时一致性。</p></li></ol><p>主从同步使得从服务器可以承担读操作，减轻主服务器的负载，同时也可以在主服务器遇到故障时，进行故障转移。</p><h2 id=主从哨兵架构><strong>主从哨兵架构</strong>
<a class=anchor href=#%e4%b8%bb%e4%bb%8e%e5%93%a8%e5%85%b5%e6%9e%b6%e6%9e%84>#</a></h2><p>Redis哨兵（Sentinel）系统是用于管理多个Redis服务器的系统。该体系结构具有以下特点：</p><ol><li><strong>监控</strong>：哨兵会监控主从服务器是否正常运作。</li><li><strong>自动故障转移</strong>：如果主服务器出现故障，哨兵可以自动选举新的主服务器，并让原来的从服务器指向新的主服务器。</li><li><strong>配置提供者</strong>：哨兵还会将当前的主服务器地址提供给客户端，确保客户端总是连接到正确的主服务器。</li></ol><p>哨兵机制通过这些功能增加了Redis环境的高可用性和稳定性。</p><h2 id=切片集群-redis-cluster><strong>切片集群-Redis Cluster</strong>
<a class=anchor href=#%e5%88%87%e7%89%87%e9%9b%86%e7%be%a4-redis-cluster>#</a></h2><p>Redis Cluster是Redis的分布式解决方案。它支持数据的水平分片，以下是其关键特性：</p><ol><li><strong>自动分片</strong>：��动将数据分布在不同的节点上，每个节点只保存整个数据集的一部分。</li><li><strong>高可用性</strong>：采用主从复制模型，即使在多个节点失败的情况下也能保证服务的可用性。</li><li><strong>无中心设计</strong>：没有中心节点，每个节点都保存着整个集群状态的一部分，节点之间通过Gossip协议交换信息。</li></ol><p>Redis Cluster通过对键进行CRC16计算并对16384取余数来决定将键分配到哪个槽位，每个节点负责一部分槽位，从而实现负载均衡。</p><h2 id=redis-cluster-通信开销><strong>Redis Cluster 通信开销</strong>
<a class=anchor href=#redis-cluster-%e9%80%9a%e4%bf%a1%e5%bc%80%e9%94%80>#</a></h2><p>由于Redis Cluster节点间需要频繁地交换消息以维护集群状态，因此会产生额外的通信开销：</p><ol><li><strong>Gossip通信</strong>：节点间通过Gossip协议定期交换信息，包括数据迁移、故障检测等。</li><li><strong>重定向操作</strong>：客户端可能会尝试向不包含数据所在槽的节点发起请求，这时节点会返回一个重定向信息</li></ol><p><strong>切片集群-Codis</strong></p><p>Redis 分布式锁是一种用于多个计算节点之间同步访问共享资源的机制。在分布式系统中，当多个进程需要同时访问某些数据或执行某些任务时，为了避免竞态条件（race conditions）和数据不一致，通常需要使用分布式锁来保证在同一时间只有一个进程能够执行特定的操作。</p><h3 id=redis-分布式锁实现>Redis 分布式锁实现
<a class=anchor href=#redis-%e5%88%86%e5%b8%83%e5%bc%8f%e9%94%81%e5%ae%9e%e7%8e%b0>#</a></h3><p>一个常见的Redis分布式锁实现是使用 Redis 的 <code>SETNX</code> 命令（Set if not exists）。这个命令只在键不存在的情况下设置键的值，并且返回是否成功设置。由于 <code>SETNX</code> 是原子操作，因此可以用来实现锁的功能。</p><p>另一个更加推荐的方式是使用 Redis 2.6.12 版本引入的 <code>SET</code> 命令结合选项 <code>NX</code>（表示只有键不存在时才进行设置）和 <code>PX</code>（给键设置过期时间，单位为毫秒），这可以保证即使在客户端崩溃的情况下，锁也会在一定时间后自动释放，防止死锁。</p><h3 id=使用-redis-分布式锁的步骤>使用 Redis 分布式锁的步骤：
<a class=anchor href=#%e4%bd%bf%e7%94%a8-redis-%e5%88%86%e5%b8%83%e5%bc%8f%e9%94%81%e7%9a%84%e6%ad%a5%e9%aa%a4>#</a></h3><ol><li><p>尝试获取锁</p><ul><li>使用 <code>SET key value NX PX milliseconds</code> 进行设置。</li><li>如果返回 <code>OK</code>，则获取锁成功。</li><li>如果返回 <code>nil</code>，则获取锁失败。</li></ul></li><li><p>执行业务逻辑</p><ul><li>在持有锁的时间内执行必要的操作。</li></ul></li><li><p>释放锁</p><ul><li>完成操作后，使用 <code>DEL</code> 命令删除键来释放锁。</li></ul></li></ol><h3 id=注意事项>注意事项
<a class=anchor href=#%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9>#</a></h3><ul><li><strong>安全性</strong>：为了确保只有获得锁的客户端能够释放锁，应该将锁和一个随机生成的值关联起来，并在解锁时验证这个值。</li><li><strong>死锁</strong>：锁应该有一个合理的过期时间，以避免客户端在持有锁时崩溃导致其他客户端永远无法获取锁。</li><li><strong>重入性</strong>：在某些场景下，同一个客户端可能需要再次获取已经持有的锁，需要考虑锁的重入性设计。</li></ul><h3 id=python-实现示例>Python 实现示例
<a class=anchor href=#python-%e5%ae%9e%e7%8e%b0%e7%a4%ba%e4%be%8b>#</a></h3><p>以下是使用 Python 和 <code>redis-py</code> 库实现 Redis 分布式锁的简单示例：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#f92672>import</span> redis
</span></span><span style=display:flex><span><span style=color:#f92672>import</span> uuid
</span></span><span style=display:flex><span><span style=color:#f92672>import</span> time
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>class</span> <span style=color:#a6e22e>RedisLock</span>:
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>def</span> __init__(self, redis_client, lock_key):
</span></span><span style=display:flex><span>        self<span style=color:#f92672>.</span>redis_client <span style=color:#f92672>=</span> redis_client
</span></span><span style=display:flex><span>        self<span style=color:#f92672>.</span>lock_key <span style=color:#f92672>=</span> lock_key
</span></span><span style=display:flex><span>        self<span style=color:#f92672>.</span>lock_value <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>def</span> <span style=color:#a6e22e>acquire_lock</span>(self, timeout_ms<span style=color:#f92672>=</span><span style=color:#ae81ff>5000</span>):
</span></span><span style=display:flex><span>        <span style=color:#e6db74>&#34;&#34;&#34;尝试获取锁&#34;&#34;&#34;</span>
</span></span><span style=display:flex><span>        self<span style=color:#f92672>.</span>lock_value <span style=color:#f92672>=</span> str(uuid<span style=color:#f92672>.</span>uuid4())  <span style=color:#75715e># 生成唯一标识符作为锁的值</span>
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> self<span style=color:#f92672>.</span>redis_client<span style=color:#f92672>.</span>set(self<span style=color:#f92672>.</span>lock_key, self<span style=color:#f92672>.</span>lock_value, nx<span style=color:#f92672>=</span><span style=color:#66d9ef>True</span>, px<span style=color:#f92672>=</span>timeout_ms)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>def</span> <span style=color:#a6e22e>release_lock</span>(self):
</span></span><span style=display:flex><span>        <span style=color:#e6db74>&#34;&#34;&#34;释放锁&#34;&#34;&#34;</span>
</span></span><span style=display:flex><span>        <span style=color:#75715e># Lua 脚本，用于检查和删除键，保证操作的原子性</span>
</span></span><span style=display:flex><span>        script <span style=color:#f92672>=</span> <span style=color:#e6db74>&#34;&#34;&#34;
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        if redis.call(&#34;get&#34;, KEYS[1]) == ARGV[1] then
</span></span></span><span style=display:flex><span><span style=color:#e6db74>            return redis.call(&#34;del&#34;, KEYS[1])
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        else
</span></span></span><span style=display:flex><span><span style=color:#e6db74>            return 0
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        end
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        &#34;&#34;&#34;</span>
</span></span><span style=display:flex><span>        result <span style=color:#f92672>=</span> self<span style=color:#f92672>.</span>redis_client<span style=color:#f92672>.</span>eval(script, <span style=color:#ae81ff>1</span>, self<span style=color:#f92672>.</span>lock_key, self<span style=color:#f92672>.</span>lock_value)
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> result <span style=color:#f92672>==</span> <span style=color:#ae81ff>1</span>  <span style=color:#75715e># 如果删除成功，则返回 True</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#75715e># 创建 Redis 客户端</span>
</span></span><span style=display:flex><span>client <span style=color:#f92672>=</span> redis<span style=color:#f92672>.</span>Redis(host<span style=color:#f92672>=</span><span style=color:#e6db74>&#39;localhost&#39;</span>, port<span style=color:#f92672>=</span><span style=color:#ae81ff>6379</span>, db<span style=color:#f92672>=</span><span style=color:#ae81ff>0</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#75715e># 使用 RedisLock</span>
</span></span><span style=display:flex><span>lock <span style=color:#f92672>=</span> RedisLock(client, <span style=color:#e6db74>&#34;my_distributed_lock&#34;</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>if</span> lock<span style=color:#f92672>.</span>acquire_lock(timeout_ms<span style=color:#f92672>=</span><span style=color:#ae81ff>10000</span>):  <span style=color:#75715e># 尝试获取锁，设置10秒过期</span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>try</span>:
</span></span><span style=display:flex><span>        <span style=color:#75715e># 执行你的业务逻辑</span>
</span></span><span style=display:flex><span>        print(<span style=color:#e6db74>&#34;Lock acquired. Doing some work...&#34;</span>)
</span></span><span style=display:flex><span>        time<span style=color:#f92672>.</span>sleep(<span style=color:#ae81ff>2</span>)
</span></span></code></pre></div></article><footer class=book-footer><div class="flex flex-wrap justify-between"></div><script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){if(window.getSelection().toString())return;e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script></footer><div class=book-comments></div><label for=menu-control class="hidden book-menu-overlay"></label></div><aside class=book-toc><div class=book-toc-content><nav id=TableOfContents><ul><li><a href=#主从架构-主从同步><strong>主从架构-主从同步</strong></a></li><li><a href=#主从哨兵架构><strong>主从哨兵架构</strong></a></li><li><a href=#切片集群-redis-cluster><strong>切片集群-Redis Cluster</strong></a></li><li><a href=#redis-cluster-通信开销><strong>Redis Cluster 通信开销</strong></a><ul><li><a href=#redis-分布式锁实现>Redis 分布式锁实现</a></li><li><a href=#使用-redis-分布式锁的步骤>使用 Redis 分布式锁的步骤：</a></li><li><a href=#注意事项>注意事项</a></li><li><a href=#python-实现示例>Python 实现示例</a></li></ul></li></ul></nav></div></aside></main></body></html>